﻿// author : David Janacek, dj-soft@seznam.cz, 2013-12-13
// company: Asseco Solution, a.s. CZ
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using Noris.LCS.AIDCCodes.Common;

namespace Noris.LCS.AIDCCodes.BarCodes
{
    #region Codabar
    /// <summary>
    ///  Codabar encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderCodabar : BarEncoderCommon, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType>  EncoderForTypes{	get {  return new CodeType[] { CodeType.Codabar }; }}
        protected override Dictionary<string, string> CreateCodeTable()
        {
            Dictionary<string, string> codeTable = new Dictionary<string, string>();
            codeTable.Add("0", "101010011");   //"101001101101");
            codeTable.Add("1", "101011001");   //"110100101011");
            codeTable.Add("2", "101001011");   //"101100101011");
            codeTable.Add("3", "110010101");   //"110110010101");
            codeTable.Add("4", "101101001");   //"101001101011");
            codeTable.Add("5", "110101001");   //"110100110101");
            codeTable.Add("6", "100101011");   //"101100110101");
            codeTable.Add("7", "100101101");   //"101001011011");
            codeTable.Add("8", "100110101");   //"110100101101");
            codeTable.Add("9", "110100101");   //"101100101101");
            codeTable.Add("-", "101001101");   //"110101001011");
            codeTable.Add("$", "101100101");   //"101101001011");
            codeTable.Add(":", "1101011011");  //"110110100101");
            codeTable.Add("/", "1101101011");  //"101011001011");
            codeTable.Add(".", "1101101101");  //"110101100101");
            codeTable.Add("+", "101100110011");//"101101100101");
            codeTable.Add("A", "1011001001");  //"110110100101");
            codeTable.Add("B", "1010010011");  //"101011001011");
            codeTable.Add("C", "1001001011");  //"110101100101");
            codeTable.Add("D", "1010011001");  //"101101100101");
            codeTable.Add("a", "1011001001");  //"110110100101");
            codeTable.Add("b", "1010010011");  //"101011001011");
            codeTable.Add("c", "1001001011");  //"110101100101");
            codeTable.Add("d", "1010011001");  //"101101100101");
            return codeTable;
        }
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            if (String.IsNullOrEmpty(content))
                throw new InvalidProgramException("Codabar can not encode empty string.");
            int length = content.Length;
            if (length < 2)
                throw new InvalidProgramException("Codabar can not encode string \"" + content + "\" of length smaller than 2.");
            if (!ContainOnly(content[0], "abcdABCD"))
                throw new InvalidProgramException("Codabar can not encode string \"" + content + "\", contain invalid START character.");
            if (!ContainOnly(content[length - 1], "abcdABCD"))
                throw new InvalidProgramException("Codabar can not encode string \"" + content + "\", contain invalid STOP character.");
            if (!ContainOnlyCodeTable(content))
                throw new InvalidProgramException("Codabar can not encode string \"" + content + "\", contain not supported characters.");
        }
        protected override void Encode(string content, CodeType codeType)
        {
            int length = content.Length;
            for (int i = 0; i < length; i++)
            {
                char c = content[i];
                this.AddCodeToBuffer(c.ToString());
                this.AddCharToLabel(c, 30, 6);
                if (i < (length - 1))
                    this.AddTextToBuffer("0");
            }

            this.CreateMatrixFromBuffer(content, codeType, 30, 4, 4, 4, 6);   // Matrix: 30 point height, 4 point border at X and Y direction.
        }
        #endregion
    }
    #endregion
    #region Code11
    /// <summary>
    ///  Code 11 encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderCode11 : BarEncoderCommon, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.Code11 }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {
            Dictionary<string, string> codeTable = new Dictionary<string, string>();
            codeTable.Add("0", "101011");
            codeTable.Add("1", "1101011");
            codeTable.Add("2", "1001011");
            codeTable.Add("3", "1100101");
            codeTable.Add("4", "1011011");
            codeTable.Add("5", "1101101");
            codeTable.Add("6", "1001101");
            codeTable.Add("7", "1010011");
            codeTable.Add("8", "1101001");
            codeTable.Add("9", "110101");
            codeTable.Add("-", "101101");
            codeTable.Add("$", "1011001");        // Start + Stop
            return codeTable;
        }
        /// <summary>
        /// $ Code for Start and Stop pattern
        /// </summary>
        protected const string CODE_MARGIN = "$";
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            if (String.IsNullOrEmpty(content))
                throw new InvalidProgramException("Code11 can not encode empty string.");
            if (!ContainOnly(content, "0123456789-"))
                throw new InvalidProgramException("Code11 can not encode string \"" + content + "\", contain invalid character (only numeric and '-' are supported).");
        }
        protected override void Encode(string content, CodeType codeType)
        {
            string code = content;

            #region Calculate checksum C and K
            Dictionary<char, int> values = new Dictionary<char, int>();
            values.Add('0', 0);
            values.Add('1', 1);
            values.Add('2', 2);
            values.Add('3', 3);
            values.Add('4', 4);
            values.Add('5', 5);
            values.Add('6', 6);
            values.Add('7', 7);
            values.Add('8', 8);
            values.Add('9', 9);
            values.Add('-', 10);

            code += _GetCheckSum(code, values, 1, 10);               // C checksum weights go 1-10
            if (code.Length >= 10)                                   // K checksums are recommended on any message length greater than or equal to 10
                code += _GetCheckSum(code, values, 1, 9);            // K checksum weights go 1-9
            #endregion

            code = CODE_MARGIN + code + CODE_MARGIN;                 // Start + Data + Stop
            int length = code.Length;
            for (int i = 0; i < length; i++)
            {
                char c = code[i];
                this.AddCodeToBuffer(c.ToString());                  // Start, Data, Stop

                bool isFirst = (i == 0);
                bool isLast = (i == (length - 1));
                if (!isFirst && !isLast)
                    this.AddCharToLabel(c, 30, 6);
                
                if (!isLast)
                    this.AddTextToBuffer("0");                       // Space
            }

            this.CreateMatrixFromBuffer(code, codeType, 30, 4, 4, 4, 6);   // Matrix: 30 point height, 4 point border at X and Y direction.
        }
        /// <summary>
        /// Generate checksum by rules for Code11
        /// </summary>
        /// <param name="code"></param>
        /// <param name="values"></param>
        /// <param name="weightMin"></param>
        /// <param name="weightMax"></param>
        /// <returns></returns>
        private static string _GetCheckSum(string code, Dictionary<char, int> values, int weightMin, int weightMax)
        {
            int sum = 0;
            int weight = weightMin;
            foreach (char c in code)
            {
                int value = values[c];
                sum += value * weight;
                weight = (weight < weightMax ? weight++ : weightMin);
            }
            sum = sum % 11;
            if (sum == 10) return "-";                                // Checksum 10 not coded as string "10", but as character "-":
            return sum.ToString();
        }
        #endregion
    }
    #endregion
    #region EAN13
    /// <summary>
    ///  EAN-13 encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderEAN13 : EanBase, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.EAN13 }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {
            this.PreparePatternTables(PATT_EAN13);
            return GetEanCodeTable(true, true, true, true);
        }
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            this.CheckContent(content, "EAN13", 12, 13);
        }
        protected override void Encode(string content, CodeType codeType)
        {
            string code = content;
            int length = code.Length;
            if (length == 12)
                code += GetCheckDigitEan(code, 12);

            length = code.Length;
            if (length != 13)
                throw new InvalidProgramException("EAN13 can not encode string \"" + code + "\", length must be 13 characters.");

            // First char:
            char f = code[0];                                        // Pattern selector = char at index 0
            AddCharToLabel(f, -7, 7, 26, 8);

            AddCodeToBuffer(CODE_START);                             // Start pattern

            // First 6 digits (after first digit) are coded by pattern, which is selected by value of first digit:
            string pattern = GetPattern(PATT_EAN13, f);              // subcodes (a/b) for first 6 digits (at indexes 1 to 6)
            for (int p = 1; p < 7; p++)
            {
                char c = code[p];
                string subcode = pattern[p - 1].ToString();          // "a" or "b"
                AddCodeToBuffer(c.ToString() + subcode);             // "1a", "7b" and so on
                AddCharToLabel(c, 26, 8);
            }

            AddCodeToBuffer(CODE_DIVIDER);                           // Central separator

            // Seconds 6 digits are coded by pattern "c":
            for (int p = 7; p < 13; p++)                             // second 6 digits (at indexes 7 to 12)
            {
                char c = code[p];
                AddCodeToBuffer(c.ToString() + "c");                 // "1c"
                AddCharToLabel(c, 26, 8);
            }

            AddCodeToBuffer(CODE_STOP);                              // Stop pattern

            // Matrix: 30 point height, 7 point border at X (label for char [0] is before code!) and 4 point at Y direction, specific convertor
            this.CreateMatrixFromBuffer(code, codeType, 30, 7, 4, 7, 4, 
                (x, y, c) => this.CodeCharConvertor(x, y, 26, c));
        }
        #endregion
    }
    #endregion
    #region EAN8
    /// <summary>
    ///  EAN-8 encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderEAN8 : EanBase, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.EAN8 }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {
            return GetEanCodeTable(true, false, true, true);
        }
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            this.CheckContent(content, "EAN8", 7, 8);
        }
        protected override void Encode(string content, CodeType codeType)
        {
            string code = content;
            int length = code.Length;
            if (length == 7)
                code += GetCheckDigitEan(code, 7);

            length = code.Length;
            if (length != 8)
                throw new InvalidProgramException("EAN8 can not encode string \"" + code + "\", length must be 8 characters.");

            AddCodeToBuffer(CODE_START);                             // Start pattern

            // First 4 digits (after first digit) by pattern "A":
            for (int p = 0; p < 4; p++)
            {
                char c = code[p];
                AddCodeToBuffer(c.ToString() + "a");                 // "1a", "7a" and so on
                AddCharToLabel(c, 26, 8);
            }

            AddCodeToBuffer(CODE_DIVIDER);                           // Central separator

            // Seconds 4 digits are coded by pattern "c":
            for (int p = 4; p < 8; p++)
            {
                char c = code[p];
                AddCodeToBuffer(c.ToString() + "c");                 // "1c"
                AddCharToLabel(c, 26, 8);
            }

            AddCodeToBuffer(CODE_STOP);                              // Stop pattern

            // Matrix: 30 point height, 4 point border at all directions, specific convertor (solve length of bars by pattern for separator and data)
            this.CreateMatrixFromBuffer(code, codeType, 30, 4, 4, 4, 4,
                (x, y, c) => this.CodeCharConvertor(x, y, 26, c));
        }
        #endregion
    }
    #endregion
    #region UPC-A
    /// <summary>
    ///  UPC-A encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderUpcA : EanBase, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.UPC_A }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {
            return GetEanCodeTable(true, false, true, true);
        }
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            this.CheckContent(content, "UPC-A", 11, 12);
        }
        protected override void Encode(string content, CodeType codeType)
        {
            string code = content;
            int length = code.Length;
            if (length == 11)
                code += GetCheckDigitEan(code, 11);

            length = code.Length;
            if (length != 12)
                throw new InvalidProgramException("UPC-A can not encode string \"" + code + "\", length must be 12 characters.");
            
            AddCodeToBuffer(CODE_START);                             // Start pattern

            // First 6 digits (after first digit) by pattern "A":
            for (int p = 0; p < 6; p++)
            {
                char c = code[p];
                AddCodeToBuffer(c.ToString() + "a");                 // "1a", "7a" and so on
                AddCharToLabel(c, 26, 8);                            // Add character to CodeLabel, at X position by last inserted code, at Y position (26 + 8).
            }

            AddCodeToBuffer(CODE_DIVIDER);                           // Central separator

            // Seconds 6 digits are coded by pattern "c":
            for (int p = 6; p < 12; p++)
            {
                char c = code[p];
                AddCodeToBuffer(c.ToString() + "c");                 // "1c"
                AddCharToLabel(c, 26, 8);
            }

            AddCodeToBuffer(CODE_STOP);                              // Stop pattern

            // Matrix: 30 point height, 4 point border at all directions, specific convertor (solve length of bars by pattern for separator and data)
            this.CreateMatrixFromBuffer(code, codeType, 30, 4, 4, 4, 4,
                (x, y, c) => this.CodeCharConvertor(x, y, 26, c));
        }
        #endregion
    }
    #endregion
    #region UPC-E
    /// <summary>
    ///  UPC-E encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderUpcE : EanBase, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.UPC_E }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {
            this.PreparePatternTables(PATT_EAN13, PATT_UPCE0, PATT_UPCE1);
            
            // Prepare EAN code table for pattern A,B,C; but not separator: these will be special:
            Dictionary<string, string> codeTable = GetEanCodeTable(true, true, true, false);
            codeTable.Add(CODE_START, "202");
            codeTable.Add(CODE_STOP, "020202");
            return codeTable;
        }
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            this.CheckContent(content, "UPC-E", 6, 8, 12);
        }
        protected override void Encode(string content, CodeType codeType)
        {
            string code = content;
            int length = code.Length;
            char numberSystem = content[0];                // first digit = system
            char checkDigit = content[length - 1];         // last digit (from original code) = check digit

            if (length == 12)
                code = ConvertUpcAtoUpcE(content);

            AddCodeToBuffer(CODE_START);                   // Start pattern ("202")

            string pattern = GetPattern((numberSystem == '0' ? PATT_UPCE0 : PATT_UPCE1), checkDigit);
            int pLen = pattern.Length;
            length = code.Length;
            for (int i = 0; i < length; i++)
            {
                char c = code[i];
                char p = pattern[i % pLen];                // 'a' or 'b', by pattern content (for example: "babbaa")
                AddCodeToBuffer(c.ToString() + p.ToString());        // "1a", "7a" and so on
                AddCharToLabel(c, 26, 8);                  // Add character to CodeLabel, at X position by last inserted code, at Y position (26 + 8).
            }

            AddCodeToBuffer(CODE_STOP);                    // Stop pattern ("020202")

            // Matrix: 30 point height, 4 point border at all directions, specific convertor (solve length of bars by pattern for separator and data)
            this.CreateMatrixFromBuffer(code, codeType, 30, 4, 4, 4, 4,
                (x, y, c) => this.CodeCharConvertor(x, y, 26, c));
        }
        /// <summary>
        /// Convert a 12-digit code (UPC-A) to 6-digit code (UPC-E).
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        private string ConvertUpcAtoUpcE(string content)
        {
            char numberSystem = content[0];
            string result = "";

            //break apart into components
            string manufacturer = content.Substring(1, 5);
            string productCode = content.Substring(6, 5);

            //check for a valid number system
            if (numberSystem != '0' && numberSystem != '1')
                throw new InvalidProgramException("UPC-E can not encode string \"" + content + "\", code with length = 12 (UPC-A) must be in number system 0 or 1. Unable to convert.");

            int productNumber = Int32.Parse(productCode);
            if (manufacturer.EndsWith("000") || manufacturer.EndsWith("100") || manufacturer.EndsWith("200") && productNumber <= 999)
            {   //rule 1
                result += manufacturer.Substring(0, 2);    //first two of manufacturer
                result += productCode.Substring(2, 3);     //last three of product
                result += manufacturer[2].ToString();      //third of manufacturer
            }
            else if (manufacturer.EndsWith("00") && productNumber <= 99)
            {   //rule 2
                result += manufacturer.Substring(0, 3);    //first three of manufacturer
                result += productCode.Substring(3, 2);     //last two of product
                result += "3";                             //number 3
            }
            else if (manufacturer.EndsWith("0") && productNumber <= 9)
            {   //rule 3
                result += manufacturer.Substring(0, 4);    //first four of manufacturer
                result += productCode[4];                  //last digit of product
                result += "4";                             //number 4
            }
            else if (!manufacturer.EndsWith("0") && productNumber <= 9 && productNumber >= 5)
            {   //rule 4
                result += manufacturer;                    //manufacturer
                result += productCode[4];                  //last digit of product
            }
            else
                throw new InvalidProgramException("UPC-E can not encode string \"" + content + "\", code with length = 12 (UPC-A) is not valid for convert to UPC-E. Unable to convert.");

            return result;
        }
        #endregion
    }
    #endregion
    #region Standard2of5 + Industrial2of5
    /// <summary>
    ///  Standard2of5 encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderStandard2of5 : BarEncoderCommon, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.Standard2of5, CodeType.Industrial2of5 }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {
            Dictionary<string, string> codeTable = new Dictionary<string, string>();
            codeTable.Add("0", "11101010101110");
            codeTable.Add("1", "10111010101110");
            codeTable.Add("2", "11101110101010");
            codeTable.Add("3", "10101110101110");
            codeTable.Add("4", "11101011101010");
            codeTable.Add("5", "10111011101010");
            codeTable.Add("6", "10101011101110");
            codeTable.Add("7", "10101110111010");
            codeTable.Add("8", "11101010111010");
            codeTable.Add("9", "10111010111010");

            codeTable.Add(CODE_START, "22022010");                   // Start
            codeTable.Add(CODE_STOP, "2202022");                     // Stop

            return codeTable;
        }
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            if (String.IsNullOrEmpty(content))
                throw new InvalidProgramException("Standard2of5 can not encode empty string.");
            if (!ContainOnly(content, "0123456789"))
                throw new InvalidProgramException("Standard2of5 can not encode string \"" + content + "\", contain invalid character (only numeric are supported).");
        }
        protected override void Encode(string content, CodeType codeType)
        {
            string code = content;
            
            AddCodeToBuffer(CODE_START);                             // Start pattern
            foreach (char c in code)
            {
                AddCodeToBuffer(c.ToString());
                AddCharToLabel(c, 26, 6);
            }
            AddCodeToBuffer(CODE_STOP);                              // Stop pattern

            // Matrix: 30 point height, 4 point border at all directions, specific convertor (solve length of bars by pattern for separator and data)
            this.CreateMatrixFromBuffer(code, codeType, 30, 4, 4, 4, 4,
                (x, y, c) => this.CodeCharConvertor(x, y, 26, c));
        }
        #endregion
    }
    #endregion
    #region Interleaved2of5
    /// <summary>
    ///  Interleaved2of5 encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderInterleaved2of5 : InterleavedBase, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.Interleaved2of5 }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {
            return GetInterleavedCodeTable(true, true);
        }
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            if (String.IsNullOrEmpty(content))
                throw new InvalidProgramException("Interleaved2of5 can not encode empty string.");
            if (!ContainOnly(content, "0123456789"))
                throw new InvalidProgramException("Interleaved2of5 can not encode string \"" + content + "\", contain invalid character (only numeric are supported).");
            if ((content.Length % 2) != 0)
                throw new InvalidProgramException("Interleaved2of5 can not encode string \"" + content + "\", length (" + content.Length.ToString() + ") must be even (divisible by two).");
        }
        protected override void Encode(string content, CodeType codeType)
        {
            string code = content;
            
            AddCodeToBuffer(CODE_START);                             // Start pattern
            for (int i = 0; i < code.Length; i += 2)
            {   // One loop = two character:
                char c0 = code[i];
                char c1 = code[i + 1];
                string pattern = this.EncodeInterleaved(c0, c1);

                this.AddTextToBuffer(pattern);                       // Pattern for pair of chars
                this.AddCharsToLabel(c0, c1, 26, 6);                 // Label for two chars
            }
            AddCodeToBuffer(CODE_STOP);                              // Stop pattern

            // Matrix: 30 point height, 4 point border at all directions, specific convertor (solve length of bars by pattern for separator and data)
            this.CreateMatrixFromBuffer(code, codeType, 30, 4, 4, 4, 4,
                (x, y, c) => this.CodeCharConvertor(x, y, 26, c));
        }
        #endregion
    }
    #endregion
    #region ITF14
    /// <summary>
    ///  ITF14 encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderITF14 : InterleavedBase, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.ITF14 }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {
            return GetInterleavedCodeTable(true, true);
        }
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            if (String.IsNullOrEmpty(content))
                throw new InvalidProgramException("ITF14 can not encode empty string.");
            if (!ContainOnly(content, "0123456789"))
                throw new InvalidProgramException("ITF14 can not encode string \"" + content + "\", contain invalid character (only numeric are supported).");
            if (!(content.Length == 13 || content.Length == 14))
                throw new InvalidProgramException("ITF14 can not encode string \"" + content + "\", length (" + content.Length.ToString() + ") must be 13 or 14 character.");
        }
        protected override void Encode(string content, CodeType codeType)
        {
            string code = content;
            if (code.Length == 13)
                code += GetCheckDigitItf14(code, 13);

            AddCodeToBuffer(CODE_START);                             // Start pattern
            for (int i = 0; i < code.Length; i += 2)
            {   // One loop = two character:
                char c0 = code[i];
                char c1 = code[i + 1];
                string pattern = this.EncodeInterleaved(c0, c1);

                this.AddTextToBuffer(pattern);                       // Pattern for pair of chars
                this.AddCharsToLabel(c0, c1, 26, 6);                 // Label for two chars
            }
            this.AddCodeToBuffer(CODE_STOP);                         // Stop pattern

            // Matrix: 30 point height, 4 point border at all directions, specific convertor (solve length of bars by pattern for separator and data)
            this.CreateMatrixFromBuffer(code, codeType, 30, 4, 4, 4, 4,
                (x, y, c) => this.CodeCharConvertor(x, y, 26, c));
        }
        /// <summary>
        /// Return CheckDigit
        /// </summary>
        /// <param name="code"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        private string GetCheckDigitItf14(string code, int length)
        {
            return GetCheckDigitOddEven(code, 13, 3, 1);
        }
        #endregion
    }
    #endregion
    #region Code39
    /// <summary>
    ///  Code39 encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderCode39 : BarEncoderCommon, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.Code39 }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {
            Dictionary<string, string> codeTable = new Dictionary<string, string>();
            codeTable.Add("0", "101001101101");
            codeTable.Add("1", "110100101011");
            codeTable.Add("2", "101100101011");
            codeTable.Add("3", "110110010101");
            codeTable.Add("4", "101001101011");
            codeTable.Add("5", "110100110101");
            codeTable.Add("6", "101100110101");
            codeTable.Add("7", "101001011011");
            codeTable.Add("8", "110100101101");
            codeTable.Add("9", "101100101101");
            codeTable.Add("A", "110101001011");
            codeTable.Add("B", "101101001011");
            codeTable.Add("C", "110110100101");
            codeTable.Add("D", "101011001011");
            codeTable.Add("E", "110101100101");
            codeTable.Add("F", "101101100101");
            codeTable.Add("G", "101010011011");
            codeTable.Add("H", "110101001101");
            codeTable.Add("I", "101101001101");
            codeTable.Add("J", "101011001101");
            codeTable.Add("K", "110101010011");
            codeTable.Add("L", "101101010011");
            codeTable.Add("M", "110110101001");
            codeTable.Add("N", "101011010011");
            codeTable.Add("O", "110101101001");
            codeTable.Add("P", "101101101001");
            codeTable.Add("Q", "101010110011");
            codeTable.Add("R", "110101011001");
            codeTable.Add("S", "101101011001");
            codeTable.Add("T", "101011011001");
            codeTable.Add("U", "110010101011");
            codeTable.Add("V", "100110101011");
            codeTable.Add("W", "110011010101");
            codeTable.Add("X", "100101101011");
            codeTable.Add("Y", "110010110101");
            codeTable.Add("Z", "100110110101");
            codeTable.Add("-", "100101011011");
            codeTable.Add(".", "110010101101");
            codeTable.Add(" ", "100110101101");
            codeTable.Add("$", "100100100101");
            codeTable.Add("/", "100100101001");
            codeTable.Add("+", "100101001001");
            codeTable.Add("%", "101001001001");
            codeTable.Add(CODE_MARGIN, "100101101101"); 
            return codeTable;
        }
        /// <summary>
        /// × Code for Start and Stop pattern
        /// </summary>
        protected const string CODE_MARGIN = "×";
        /// <summary>
        /// × $/+% Disabled characters
        /// </summary>
        protected const string DISABLED_CHARS = CODE_MARGIN; // + "$/+%";
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            if (String.IsNullOrEmpty(content))
                throw new InvalidProgramException("Code39 can not encode empty string.");
            if (ContainAnyOf(content, DISABLED_CHARS))
                throw new InvalidProgramException("Code39 can not encode string \"" + content + "\", characters '" + DISABLED_CHARS + "' is not allowed.");
            if (!ContainOnlyCodeTable(content))
                throw new InvalidProgramException("Code39 can not encode string \"" + content + "\", contain invalid character (only subset of 39 characters are supported).");
        }
        protected override void Encode(string content, CodeType codeType)
        {
            string code = content;
            
            code = CODE_MARGIN + code + CODE_MARGIN;                 // Start + Data + Stop
            int length = code.Length;
            for (int i = 0; i < length; i++)
            {
                char c = code[i];
                this.AddCodeToBuffer(c.ToString());                  // Start, Data, Stop

                bool isFirst = (i == 0);
                bool isLast = (i == (length - 1));
                if (!isFirst && !isLast)
                    this.AddCharToLabel(c, 30, 6);

                if (!isLast)
                    this.AddTextToBuffer("0");                       // Space
            }

            this.CreateMatrixFromBuffer(content, codeType, 30, 4, 4, 4, 6);   // Matrix: 30 point height, 4 point border at X and Y direction.
        }
        #endregion
    }
    #endregion
    #region Code39Ext
    /// <summary>
    ///  Code39Ext encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderCode39Ext : BarEncoderCode39, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.Code39Ext }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {
            Dictionary<string, string> codeTable = base.CreateCodeTable();
            
            CreateCodeTableAddOne(codeTable, Convert.ToChar(0).ToString(), "%U");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(1).ToString(), "$A");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(2).ToString(), "$B");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(3).ToString(), "$C");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(4).ToString(), "$D");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(5).ToString(), "$E");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(6).ToString(), "$F");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(7).ToString(), "$G");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(8).ToString(), "$H");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(9).ToString(), "$I");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(10).ToString(), "$J");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(11).ToString(), "$K");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(12).ToString(), "$L");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(13).ToString(), "$M");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(14).ToString(), "$N");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(15).ToString(), "$O");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(16).ToString(), "$P");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(17).ToString(), "$Q");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(18).ToString(), "$R");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(19).ToString(), "$S");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(20).ToString(), "$T");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(21).ToString(), "$U");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(22).ToString(), "$V");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(23).ToString(), "$W");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(24).ToString(), "$X");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(25).ToString(), "$Y");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(26).ToString(), "$Z");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(27).ToString(), "%A");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(28).ToString(), "%B");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(29).ToString(), "%C");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(30).ToString(), "%D");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(31).ToString(), "%E");
            CreateCodeTableAddOne(codeTable, "!", "/A");
            CreateCodeTableAddOne(codeTable, "\"", "/B");
            CreateCodeTableAddOne(codeTable, "#", "/C");
            CreateCodeTableAddOne(codeTable, "$", "/D");
            CreateCodeTableAddOne(codeTable, "%", "/E");
            CreateCodeTableAddOne(codeTable, "&", "/F");
            CreateCodeTableAddOne(codeTable, "'", "/G");
            CreateCodeTableAddOne(codeTable, "(", "/H");
            CreateCodeTableAddOne(codeTable, ")", "/I");
            CreateCodeTableAddOne(codeTable, "*", "/J");
            CreateCodeTableAddOne(codeTable, "+", "/K");
            CreateCodeTableAddOne(codeTable, ",", "/L");
            CreateCodeTableAddOne(codeTable, "/", "/O");
            CreateCodeTableAddOne(codeTable, ":", "/Z");
            CreateCodeTableAddOne(codeTable, ";", "%F");
            CreateCodeTableAddOne(codeTable, "<", "%G");
            CreateCodeTableAddOne(codeTable, "=", "%H");
            CreateCodeTableAddOne(codeTable, ">", "%I");
            CreateCodeTableAddOne(codeTable, "?", "%J");
            CreateCodeTableAddOne(codeTable, "[", "%K");
            CreateCodeTableAddOne(codeTable, "\\", "%L");
            CreateCodeTableAddOne(codeTable, "]", "%M");
            CreateCodeTableAddOne(codeTable, "^", "%N");
            CreateCodeTableAddOne(codeTable, "_", "%O");
            CreateCodeTableAddOne(codeTable, "{", "%P");
            CreateCodeTableAddOne(codeTable, "|", "%Q");
            CreateCodeTableAddOne(codeTable, "}", "%R");
            CreateCodeTableAddOne(codeTable, "~", "%S");
            CreateCodeTableAddOne(codeTable, "`", "%W");
            CreateCodeTableAddOne(codeTable, "@", "%V");
            CreateCodeTableAddOne(codeTable, "a", "+A");
            CreateCodeTableAddOne(codeTable, "b", "+B");
            CreateCodeTableAddOne(codeTable, "c", "+C");
            CreateCodeTableAddOne(codeTable, "d", "+D");
            CreateCodeTableAddOne(codeTable, "e", "+E");
            CreateCodeTableAddOne(codeTable, "f", "+F");
            CreateCodeTableAddOne(codeTable, "g", "+G");
            CreateCodeTableAddOne(codeTable, "h", "+H");
            CreateCodeTableAddOne(codeTable, "i", "+I");
            CreateCodeTableAddOne(codeTable, "j", "+J");
            CreateCodeTableAddOne(codeTable, "k", "+K");
            CreateCodeTableAddOne(codeTable, "l", "+L");
            CreateCodeTableAddOne(codeTable, "m", "+M");
            CreateCodeTableAddOne(codeTable, "n", "+N");
            CreateCodeTableAddOne(codeTable, "o", "+O");
            CreateCodeTableAddOne(codeTable, "p", "+P");
            CreateCodeTableAddOne(codeTable, "q", "+Q");
            CreateCodeTableAddOne(codeTable, "r", "+R");
            CreateCodeTableAddOne(codeTable, "s", "+S");
            CreateCodeTableAddOne(codeTable, "t", "+T");
            CreateCodeTableAddOne(codeTable, "u", "+U");
            CreateCodeTableAddOne(codeTable, "v", "+V");
            CreateCodeTableAddOne(codeTable, "w", "+W");
            CreateCodeTableAddOne(codeTable, "x", "+X");
            CreateCodeTableAddOne(codeTable, "y", "+Y");
            CreateCodeTableAddOne(codeTable, "z", "+Z");
            CreateCodeTableAddOne(codeTable, Convert.ToChar(127).ToString(), "%T"); //also %X, %Y, %Z 

            return codeTable;
        }
        /// <summary>
        /// Add pattern for specified code.
        /// Pattern is created as sum of patterns from replacement characters, with one space ("0") between individually character.
        /// </summary>
        /// <param name="codeTable"></param>
        /// <param name="code"></param>
        /// <param name="replacement"></param>
        protected void CreateCodeTableAddOne(Dictionary<string, string> codeTable, string code, string replacement)
        {
            string value = "";
            foreach (char c in replacement)                      // replacement = "%V"
                value += "0" + codeTable[c.ToString()];          // 0[pattern%]0[patternV] = "0" + "101001001001" + "0" + "100110101011"

            codeTable.Add(ExtendCode(code), value.Substring(1)); // «@» = [patternA]0[patternB] = "101001001001" + "0" + "100110101011"
        }
        protected string ExtendCode(string code)
        {
            return "«" + code + "»";
        }
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            if (String.IsNullOrEmpty(content))
                throw new InvalidProgramException("Code39Ext can not encode empty string.");
            if (content.Contains(CODE_MARGIN))
                throw new InvalidProgramException("Code39Ext can not encode string \"" + content + "\", character '" + CODE_MARGIN + "' is not allowed.");
            // if (!ContainOnlyCodeTable(content))
            //    throw new InvalidProgramException("Code39Ext can not encode string \"" + content + "\", contain invalid character (only subset of characters are supported).");
        }
        protected override void Encode(string content, CodeType codeType)
        {
            string code = content;
            
            code = CODE_MARGIN + code + CODE_MARGIN;                 // Start + Data + Stop
            int length = code.Length;
            for (int i = 0; i < length; i++)
            {
                char c = code[i];
                string key = c.ToString();
                if (!this.CodeTable.ContainsKey(key))
                    key = ExtendCode(key);
                if (!this.CodeTable.ContainsKey(key))
                    throw new InvalidProgramException("Code39Ext can not encode string \"" + content + "\", character '" + c.ToString() + "' is not allowed.");

                this.AddCodeToBuffer(key);                           // Start, Data, Stop

                bool isFirst = (i == 0);
                bool isLast = (i == (length - 1));
                if (!isFirst && !isLast)
                    this.AddCharToLabel(c, 30, 6);

                if (!isLast)
                    this.AddTextToBuffer("0");                       // Space
            }

            this.CreateMatrixFromBuffer(content, codeType, 30, 4, 4, 4, 6);   // Matrix: 30 point height, 4 point border at X and Y direction.
        }
        #endregion
    }
    #endregion 
    #region Pharmacode
    /// <summary>
    ///  Pharmacode encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderPharmacode : BarEncoderCommon, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.Pharmacode }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {
            Dictionary<string, string> codeTable = new Dictionary<string, string>();
            return codeTable;
        }
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            if (String.IsNullOrEmpty(content))
                throw new InvalidProgramException("Pharmacode can not encode empty string.");
            if (!ContainOnly(content, "0123456789"))
                throw new InvalidProgramException("Pharmacode can not encode string \"" + content + "\", contain invalid character (only numeric are supported).");
            int length = content.Length;
            if (length > 6)
                throw new InvalidProgramException("Pharmacode can not encode string \"" + content + "\", length exceeds 6 characters.");
            int value;
            if (!Int32.TryParse(content, out value))
                throw new InvalidProgramException("Pharmacode can not encode string \"" + content + "\", not a number.");
            if (value < 3 || value > 131070)
                throw new InvalidProgramException("Pharmacode can not encode string \"" + content + "\", value is out of range (3 to 131070).");
        }
        protected override void Encode(string content, CodeType codeType)
        {
            int value;
            if (!Int32.TryParse(content, out value))
                throw new InvalidProgramException("Pharmacode can not encode string \"" + content + "\", not a number.");

            #region Pharmacode samples
            /* Pharmacode   value = nnww (narow / wide bar):
             3 = nn
             4 = nw
             5 = wn
             6 = ww
             7 = nnn
             8 = nnw
             9 = nwn
            10 = nww
            11 = wnn
            12 = wnw
            13 = wwn
            14 = www
            15 = nnnn
            16 = nnnw
            17 = nnwn
            18 = nnww
            19 = nwnn
            20 = nwnw
            21 = nwwn
            22 = nwww
            23 = wnnn
            24 = wnnw
              ...
            30 = wwww
            31 = nnnnn
            32 = nnnnw
              ...
            61 = wwwwn
            62 = wwwww
            63 = nnnnnn
              ...
            131070 = wwwwwwwwwwwwwwww (16x w)
            */
            #endregion

            // Code:
            int code = value + 1;                          // From above is obvious: value+1, in binary form represent code: first "1" from left is "begin point", next bits (to right) are encoded as 0=n, 1=w:
            int mask = 0x20000;                            // 131072d: floating mask bit
            bool running = false;                          // true = first "1" was found in binary representation of value
            bool space = false;
            while (mask > 0)
            {
                bool bit = ((code & mask) != 0);           // One bit from 16 to 0
                if (!running)
                {   // If no running (still have "0"), and now is bit = "1", then start running:
                    if (bit) running = true;
                }
                else
                {   // If running, then bit "1" = wide, "0" = narrow:
                    this.AddTextToBuffer((space ? "00" : "") + (bit ? "111" : "1"));
                    space = true;
                }
                mask = mask >> 1;                          // Mask bit shift to right by 1
            }

            // Label:
            this.AddTextToLabel(content, 20, 5, 6);

            this.CreateMatrixFromBuffer(content, codeType, 20, 4, 4, 4, 6);   // Matrix: 30 point height, 4 point border at X and Y direction.
        }
        #endregion
    }
    #endregion
    #region Code128 + Code128A + Code128B + Code128C
    /// <summary>
    ///  Code128 + Code128A + Code128B + Code128C encoding
    ///  Written by: David Janacek
    /// </summary>
    internal class BarEncoderCode128 : BarEncoderCommon, IEncoderEngine
    {
        #region Definitions
        protected override IEnumerable<CodeType> EncoderForTypes { get { return new CodeType[] { CodeType.Code128, CodeType.Code128A, CodeType.Code128B, CodeType.Code128C }; } }
        protected override Dictionary<string, string> CreateCodeTable()
        {   // Standard method, return a void codeTable:
            Dictionary<string, string> codeTable = new Dictionary<string, string>();
            return codeTable;
        }
        protected virtual void CreateCode128Table()
        {
            AddCode128ToCodeTable(" ", " ", "00", "11011001100", 0);
            AddCode128ToCodeTable("!", "!", "01", "11001101100", 1);
            AddCode128ToCodeTable("\"", "\"", "02", "11001100110", 2);
            AddCode128ToCodeTable("#", "#", "03", "10010011000", 3);
            AddCode128ToCodeTable("$", "$", "04", "10010001100", 4);
            AddCode128ToCodeTable("%", "%", "05", "10001001100", 5);
            AddCode128ToCodeTable("&", "&", "06", "10011001000", 6);
            AddCode128ToCodeTable("'", "'", "07", "10011000100", 7);
            AddCode128ToCodeTable("(", "(", "08", "10001100100", 8);
            AddCode128ToCodeTable(")", ")", "09", "11001001000", 9);
            AddCode128ToCodeTable("*", "*", "10", "11001000100", 10);
            AddCode128ToCodeTable("+", "+", "11", "11000100100", 11);
            AddCode128ToCodeTable(",", ",", "12", "10110011100", 12);
            AddCode128ToCodeTable("-", "-", "13", "10011011100", 13);
            AddCode128ToCodeTable(".", ".", "14", "10011001110", 14);
            AddCode128ToCodeTable("/", "/", "15", "10111001100", 15);
            AddCode128ToCodeTable("0", "0", "16", "10011101100", 16);
            AddCode128ToCodeTable("1", "1", "17", "10011100110", 17);
            AddCode128ToCodeTable("2", "2", "18", "11001110010", 18);
            AddCode128ToCodeTable("3", "3", "19", "11001011100", 19);
            AddCode128ToCodeTable("4", "4", "20", "11001001110", 20);
            AddCode128ToCodeTable("5", "5", "21", "11011100100", 21);
            AddCode128ToCodeTable("6", "6", "22", "11001110100", 22);
            AddCode128ToCodeTable("7", "7", "23", "11101101110", 23);
            AddCode128ToCodeTable("8", "8", "24", "11101001100", 24);
            AddCode128ToCodeTable("9", "9", "25", "11100101100", 25);
            AddCode128ToCodeTable(":", ":", "26", "11100100110", 26);
            AddCode128ToCodeTable(";", ";", "27", "11101100100", 27);
            AddCode128ToCodeTable("<", "<", "28", "11100110100", 28);
            AddCode128ToCodeTable("=", "=", "29", "11100110010", 29);
            AddCode128ToCodeTable(">", ">", "30", "11011011000", 30);
            AddCode128ToCodeTable("?", "?", "31", "11011000110", 31);
            AddCode128ToCodeTable("@", "@", "32", "11000110110", 32);
            AddCode128ToCodeTable("A", "A", "33", "10100011000", 33);
            AddCode128ToCodeTable("B", "B", "34", "10001011000", 34);
            AddCode128ToCodeTable("C", "C", "35", "10001000110", 35);
            AddCode128ToCodeTable("D", "D", "36", "10110001000", 36);
            AddCode128ToCodeTable("E", "E", "37", "10001101000", 37);
            AddCode128ToCodeTable("F", "F", "38", "10001100010", 38);
            AddCode128ToCodeTable("G", "G", "39", "11010001000", 39);
            AddCode128ToCodeTable("H", "H", "40", "11000101000", 40);
            AddCode128ToCodeTable("I", "I", "41", "11000100010", 41);
            AddCode128ToCodeTable("J", "J", "42", "10110111000", 42);
            AddCode128ToCodeTable("K", "K", "43", "10110001110", 43);
            AddCode128ToCodeTable("L", "L", "44", "10001101110", 44);
            AddCode128ToCodeTable("M", "M", "45", "10111011000", 45);
            AddCode128ToCodeTable("N", "N", "46", "10111000110", 46);
            AddCode128ToCodeTable("O", "O", "47", "10001110110", 47);
            AddCode128ToCodeTable("P", "P", "48", "11101110110", 48);
            AddCode128ToCodeTable("Q", "Q", "49", "11010001110", 49);
            AddCode128ToCodeTable("R", "R", "50", "11000101110", 50);
            AddCode128ToCodeTable("S", "S", "51", "11011101000", 51);
            AddCode128ToCodeTable("T", "T", "52", "11011100010", 52);
            AddCode128ToCodeTable("U", "U", "53", "11011101110", 53);
            AddCode128ToCodeTable("V", "V", "54", "11101011000", 54);
            AddCode128ToCodeTable("W", "W", "55", "11101000110", 55);
            AddCode128ToCodeTable("X", "X", "56", "11100010110", 56);
            AddCode128ToCodeTable("Y", "Y", "57", "11101101000", 57);
            AddCode128ToCodeTable("Z", "Z", "58", "11101100010", 58);
            AddCode128ToCodeTable("[", "[", "59", "11100011010", 59);
            AddCode128ToCodeTable(@"\", @"\", "60", "11101111010", 60);
            AddCode128ToCodeTable("]", "]", "61", "11001000010", 61);
            AddCode128ToCodeTable("^", "^", "62", "11110001010", 62);
            AddCode128ToCodeTable("_", "_", "63", "10100110000", 63);
            AddCode128ToCodeTable("\0", "`", "64", "10100001100", 64);
            AddCode128ToCodeTable(Convert.ToChar(1).ToString(), "a", "65", "10010110000", 65);
            AddCode128ToCodeTable(Convert.ToChar(2).ToString(), "b", "66", "10010000110", 66);
            AddCode128ToCodeTable(Convert.ToChar(3).ToString(), "c", "67", "10000101100", 67);
            AddCode128ToCodeTable(Convert.ToChar(4).ToString(), "d", "68", "10000100110", 68);
            AddCode128ToCodeTable(Convert.ToChar(5).ToString(), "e", "69", "10110010000", 69);
            AddCode128ToCodeTable(Convert.ToChar(6).ToString(), "f", "70", "10110000100", 70);
            AddCode128ToCodeTable(Convert.ToChar(7).ToString(), "g", "71", "10011010000", 71);
            AddCode128ToCodeTable(Convert.ToChar(8).ToString(), "h", "72", "10011000010", 72);
            AddCode128ToCodeTable(Convert.ToChar(9).ToString(), "i", "73", "10000110100", 73);
            AddCode128ToCodeTable(Convert.ToChar(10).ToString(), "j", "74", "10000110010", 74);
            AddCode128ToCodeTable(Convert.ToChar(11).ToString(), "k", "75", "11000010010", 75);
            AddCode128ToCodeTable(Convert.ToChar(12).ToString(), "l", "76", "11001010000", 76);
            AddCode128ToCodeTable(Convert.ToChar(13).ToString(), "m", "77", "11110111010", 77);
            AddCode128ToCodeTable(Convert.ToChar(14).ToString(), "n", "78", "11000010100", 78);
            AddCode128ToCodeTable(Convert.ToChar(15).ToString(), "o", "79", "10001111010", 79);
            AddCode128ToCodeTable(Convert.ToChar(16).ToString(), "p", "80", "10100111100", 80);
            AddCode128ToCodeTable(Convert.ToChar(17).ToString(), "q", "81", "10010111100", 81);
            AddCode128ToCodeTable(Convert.ToChar(18).ToString(), "r", "82", "10010011110", 82);
            AddCode128ToCodeTable(Convert.ToChar(19).ToString(), "s", "83", "10111100100", 83);
            AddCode128ToCodeTable(Convert.ToChar(20).ToString(), "t", "84", "10011110100", 84);
            AddCode128ToCodeTable(Convert.ToChar(21).ToString(), "u", "85", "10011110010", 85);
            AddCode128ToCodeTable(Convert.ToChar(22).ToString(), "v", "86", "11110100100", 86);
            AddCode128ToCodeTable(Convert.ToChar(23).ToString(), "w", "87", "11110010100", 87);
            AddCode128ToCodeTable(Convert.ToChar(24).ToString(), "x", "88", "11110010010", 88);
            AddCode128ToCodeTable(Convert.ToChar(25).ToString(), "y", "89", "11011011110", 89);
            AddCode128ToCodeTable(Convert.ToChar(26).ToString(), "z", "90", "11011110110", 90);
            AddCode128ToCodeTable(Convert.ToChar(27).ToString(), "{", "91", "11110110110", 91);
            AddCode128ToCodeTable(Convert.ToChar(28).ToString(), "|", "92", "10101111000", 92);
            AddCode128ToCodeTable(Convert.ToChar(29).ToString(), "}", "93", "10100011110", 93);
            AddCode128ToCodeTable(Convert.ToChar(30).ToString(), "~", "94", "10001011110", 94);

            AddCode128ToCodeTable(Convert.ToChar(31).ToString(), Convert.ToChar(127).ToString(), "95", "10111101000", 95);
            AddCode128ToCodeTable(FNC3, FNC3, "96", "10111100010", 96);
            AddCode128ToCodeTable(FNC2, FNC2, "97", "11110101000", 97);
            AddCode128ToCodeTable(SHIFT, SHIFT, "98", "11110100010", 98);
            AddCode128ToCodeTable(CODE_C, CODE_C, "99", "10111011110", 99);
            AddCode128ToCodeTable(CODE_B, FNC4, CODE_B, "10111101110", 100);
            AddCode128ToCodeTable(FNC4, CODE_A, CODE_A, "11101011110", 101);
            AddCode128ToCodeTable(FNC1, FNC1, FNC1, "11110101110", 102);
            AddCode128ToCodeTable(START_A, START_A, START_A, "11010000100", 103);
            AddCode128ToCodeTable(START_B, START_B, START_B, "11010010000", 104);
            AddCode128ToCodeTable(START_C, START_C, START_C, "11010011100", 105);
            AddCode128ToCodeTable(STOP, STOP, STOP, "11000111010", -1);
            AddCode128ToCodeTable(END, END, END, "11", -1);
        }
        /// <summary>
        /// Return key for Code128A and specified code
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        protected static Code128Key GetCodeKeyA(string code) { return GetCodeKey(Code128Set.SetA, code); }
        /// <summary>
        /// Return key for Code128B and specified code
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        protected static Code128Key GetCodeKeyB(string code) { return GetCodeKey(Code128Set.SetB, code); }
        /// <summary>
        /// Return key for Code128C and specified code
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        protected static Code128Key GetCodeKeyC(string code) { return GetCodeKey(Code128Set.SetC, code); }
        /// <summary>
        /// Return key for Code128? (? = codeName) and specified code
        /// </summary>
        /// <param name="codeKey"></param>
        /// <returns></returns>
        protected static Code128Key GetCodeKey(Code128Set codeSet, string codeKey) { return new Code128Key(codeSet, codeKey); }

        /// <summary>CODE_A code for begin CodeA</summary>
        protected const string CODE_A = "CODE_A";
        /// <summary>CODE_B code for begin CodeB</summary>
        protected const string CODE_B = "CODE_B";
        /// <summary>CODE_C code for begin CodeC</summary>
        protected const string CODE_C = "CODE_C";
        /// <summary>START_A code for start CodeA</summary>
        protected const string START_A = "START_A";
        /// <summary>START_B code for start CodeB</summary>
        protected const string START_B = "START_B";
        /// <summary>START_C code for start CodeC</summary>
        protected const string START_C = "START_C";
        /// <summary>STOP code for stop code</summary>
        protected const string STOP = "STOP";
        /// <summary>END code for end of code</summary>
        protected const string END = "END";
        /// <summary>SHIFT code for shift code</summary>
        protected const string SHIFT = "SHIFT";

        /// <summary>FNC1 code</summary>
        protected const string FNC1 = "\xC8";       // =Convert.ToChar(200).ToString();
        /// <summary>FNC2 code</summary>
        protected const string FNC2 = "\xC9";       // =Convert.ToChar(201).ToString();
        /// <summary>FNC3 code</summary>
        protected const string FNC3 = "\xCA";       // =Convert.ToChar(202).ToString();
        /// <summary>FNC4 code</summary>
        protected const string FNC4 = "\xCB";       // =Convert.ToChar(203).ToString();
        /// <summary>
        /// Determines whether the Code128Table contains the specified key.
        /// </summary>
        /// <param name="codeSet"></param>
        /// <param name="codeKey"></param>
        /// <returns></returns>
        protected bool ContainsKey(Code128Set codeSet, string codeKey)
        {
            Code128Key key = new Code128Key(codeSet, codeKey);
            return this.ContainsKey(key);
        }
        /// <summary>
        /// Determines whether the Code128Table contains the specified key.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        protected bool ContainsKey(Code128Key key)
        {
            return this.Code128Table.ContainsKey(key);
        }
        /// <summary>
        /// Try find item for any CodeSet and Key, return true when success and store Key+Value to out value.
        /// Search sequentially for CodeSet A, B, C (in described order).
        /// </summary>
        /// <param name="codeSet"></param>
        /// <param name="codeKey"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected bool TryGetInfo(string codeKey, out KeyValuePair<Code128Key, Code128Item> value)
        {
            if (this.TryGetInfo(Code128Set.SetA, codeKey, out value)) return true;
            if (this.TryGetInfo(Code128Set.SetB, codeKey, out value)) return true;
            if (this.TryGetInfo(Code128Set.SetC, codeKey, out value)) return true;
            value = new KeyValuePair<Code128Key, Code128Item>();
            return false;
        }
        /// <summary>
        /// Try find item for specified CodeSet and Key, return true when success and store Key+Value to out value.
        /// </summary>
        /// <param name="codeSet"></param>
        /// <param name="codeKey"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected bool TryGetInfo(Code128Set codeSet, string codeKey, out KeyValuePair<Code128Key, Code128Item> value)
        {
            Code128Key key = new Code128Key(codeSet, codeKey);
            return this.TryGetInfo(key, out value);
        }
        /// <summary>
        /// Try find item for specified key, return true when success, and store Key+Value to out value.
        /// </summary>
        /// <param name="codeSet"></param>
        /// <param name="codeKey"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected bool TryGetInfo(Code128Key key, out KeyValuePair<Code128Key, Code128Item> value)
        {
            Code128Item item;
            if (this.Code128Table.TryGetValue(key, out item))
            {
                value = new KeyValuePair<Code128Key, Code128Item>(key, item);
                return true;
            }
            value = new KeyValuePair<Code128Key, Code128Item>();
            return false;
        }
        /// <summary>
        /// CodeTable for Code128: Key = { Set:Key }, Value = { Pattern, CheckDigit Value }.
        /// Autoinitializing property.
        /// </summary>
        protected Dictionary<Code128Key, Code128Item> Code128Table
        {
            get
            {
                if (this._Code128Table == null)
                    this._Code128Prepare();
                return this._Code128Table;
            }
        }
        /// <summary>
        /// CodeItems index for Code128: Key = { int Value }, Value = { Pattern, CheckDigit Value }.
        /// Autoinitializing property.
        /// </summary>
        protected Dictionary<int, Code128Item> Code128Items
        {
            get
            {
                if (this._Code128Items == null)
                    this._Code128Prepare();
                return this._Code128Items;
            }
        }
        /// <summary>
        /// Prepare Code128 tables
        /// </summary>
        protected void _Code128Prepare()
        {
            this._Code128Table = new Dictionary<Code128Key, Code128Item>();
            this._Code128Items = new Dictionary<int, Code128Item>();
            this.CreateCode128Table();
        }
        /// <summary>
        /// Add code + pattern into _Code128Table, for code A, B, C; and to index _Code128Items
        /// </summary>
        /// <param name="codeTable">table of code</param>
        /// <param name="codeA">Code for 128A</param>
        /// <param name="codeB">Code for 128B</param>
        /// <param name="codeC">Code for 128C</param>
        /// <param name="pattern">Pattern for this code</param>
        /// <param name="value">ignored</param>
        protected void AddCode128ToCodeTable(string codeA, string codeB, string codeC, string pattern, int value)
        {
            Code128Item item = new Code128Item(pattern, value);
            this._Code128Table.Add(GetCodeKeyA(codeA), item);
            this._Code128Table.Add(GetCodeKeyB(codeB), item);
            this._Code128Table.Add(GetCodeKeyC(codeC), item);
            if (value >= 0)
                this._Code128Items.Add(value, item);
        }
        private Dictionary<Code128Key, Code128Item> _Code128Table;
        private Dictionary<int, Code128Item> _Code128Items;
        #region enum Code128Set, classes Code128Key + Code128Item + Code128CheckSum
        /// <summary>
        /// CodeSet for Code128 (A,B,C)
        /// </summary>
        protected enum Code128Set
        {
            None = 0,
            SetA,
            SetB,
            SetC
        }
        /// <summary>
        /// Return a CodeSet ("A"; "B"; "C") for CodeType : Code128A; Code128B; Code128C.
        /// </summary>
        /// <param name="codeType"></param>
        /// <returns></returns>
        protected static Code128Set GetCodeSet(CodeType codeType)
        {
            switch (codeType)
            {
                case CodeType.Code128: return Code128Set.None;
                case CodeType.Code128A: return Code128Set.SetA;
                case CodeType.Code128B: return Code128Set.SetB;
                case CodeType.Code128C: return Code128Set.SetC;
            }
            throw new InvalidProgramException("Code128Key: CodeSet can not be determined for CodeType = " + codeType.ToString());
        }
        /// <summary>
        /// Key for Code128 (CodeSet + CodeKey)
        /// </summary>
        protected class Code128Key
        {
            public Code128Key(Code128Set codeSet, string codeKey)
            {
                this._Init(codeSet, codeKey);
            }
            private void _Init(Code128Set codeSet, string codeKey)
            {
                this.CodeSet = codeSet;
                this.CodeKey = codeKey;
                this._Hash = codeSet.GetHashCode() ^ codeKey.GetHashCode();
            }
            /// <summary>Set A, B, C</summary>
            public Code128Set CodeSet { get; private set; }
            /// <summary>Key</summary>
            public string CodeKey { get; private set; }
            /// <summary>Length of Key (number of characters)</summary>
            public int KeyLength { get { return this.CodeKey.Length; } }
            private int _Hash;
            public override int GetHashCode()
            {
                return this._Hash;
            }
            public override bool Equals(object obj)
            {
                Code128Key other = obj as Code128Key;
                if (other == null) return false;
                return other.CodeSet == this.CodeSet && other.CodeKey == this.CodeKey;
            }
            public override string ToString()
            {
                return this.CodeSet.ToString() + ": " + this.CodeKey;
            }
        }
        /// <summary>
        /// Value for Code128 (Pattern + CheckDigit Value)
        /// </summary>
        protected class Code128Item
        {
            /// <summary>
            /// Constructor
            /// </summary>
            /// <param name="pattern"></param>
            /// <param name="value"></param>
            public Code128Item(string pattern, int value)
            {
                this.Pattern = pattern;
                this.Value = value;
            }
            /// <summary>
            /// Text pattern
            /// </summary>
            public string Pattern { get; private set; }
            /// <summary>
            /// Value for CheckDigit
            /// </summary>
            public int Value { get; private set; }
            public override string ToString()
            {
                return this.Pattern;
            }
        }
        /// <summary>
        /// Acumulator for CheckSum calculation
        /// </summary>
        protected class Code128CheckSum
        {
            public Code128CheckSum()
            {
                this._Position = 0;
                this._CheckSum = 0;
            }
            /// <summary>
            /// Sum of all current values * its positions
            /// </summary>
            public int CheckSum { get { return this._CheckSum; } }
            /// <summary>
            /// Check number (= CheckSum % 103)
            /// </summary>
            public int CheckNumber { get { return this._CheckSum % 103; } }
            private int _Position;
            private int _CheckSum;
            /// <summary>
            /// Add a value to CheckSum.
            /// Value is multiplied by (order number - 1) of calling this method: first and second = 1, third = 2, fourth = 3, and so on.
            /// </summary>
            /// <param name="value"></param>
            public void Add(int value)
            {
                int coef = (this._Position == 0 ? 1 : this._Position);
                this._CheckSum += value * coef;
                this._Position++;
            }
        }
        #endregion
        #endregion
        #region Encoding
        protected override void CheckContent(string content, CodeType codeType)
        {
            if (String.IsNullOrEmpty(content))
                throw new InvalidProgramException("Code128 can not encode empty string.");
            if (codeType == CodeType.Code128C && !ContainOnly(content, "0123456789"))
                throw new InvalidProgramException("Code128C can not encode string \"" + content + "\", only digits are allowed.");
        }
        protected override void Encode(string content, CodeType codeType)
        {
            this.Keys = new List<Code128Key>();

            Code128Set codeSet = GetCodeSet(codeType);               // Required CodeSet (Encoding CodeType A/B/C)
            Code128Set currSet = Code128Set.None;                    // Currently active set

            string code = content;
            int length = code.Length;
            if (codeType == CodeType.Code128C && ((length % 2) != 0))
            {   // Code128C can encode even number of characters, add leading "0" when necessary:
                code = "0" + code;
                length = code.Length;
            }

            Code128CheckSum checkSum = new Code128CheckSum();
            int i = 0;
            while (i < length)
            {
                KeyValuePair<Code128Key, Code128Item> value = this.GetEncodeInfo(code, i, codeSet, currSet);

                if (value.Key.CodeSet != currSet)
                    currSet = this.StoreCodeSetToBuffer(codeSet, currSet, value.Key.CodeSet, i, checkSum);

                this.StoreItemToBuffer(value.Key, value.Value, checkSum);

                i += value.Key.KeyLength;
            }

            this.AddItemForCheckDigit(checkSum);
            currSet = (currSet == Code128Set.None ? Code128Set.SetA : currSet);
            this.StoreItemToBuffer(currSet, STOP, null);
            this.StoreItemToBuffer(currSet, END, null);
            this.AddTextToLabel(content, 30, 6, 6, 6);               // Add whole label to CodeLabel, at position y=30, spreaded with X-margins=6, with height=6

            this.CreateMatrixFromBuffer(content, codeType, 30, 4, 4, 4, 6);   // Matrix: 30 point height, 4 point border at X and Y direction.
        }
        /// <summary>
        /// This method determines what codeset will be used for the following character (in the text code at position i)
        /// at the desired set "codeSet" (may be variable, if specified: None) at the applicable current set "currSet"
        /// (to reduce the frequent alternation sets, that is not necessary).
        /// Return a KeyValuePair containing a Key = next CodeSet + CodeKey (CodeKey = part of input content, 1 or 2 characters length);
        /// and Value, containig BarCode Pattern and CheckDigit Value.
        /// </summary>
        /// <param name="code"></param>
        /// <param name="i"></param>
        /// <param name="codeSet"></param>
        /// <param name="currSet"></param>
        /// <returns></returns>
        protected KeyValuePair<Code128Key, Code128Item> GetEncodeInfo(string code, int i, Code128Set codeSet, Code128Set currSet)
        {
            KeyValuePair<Code128Key, Code128Item> result;
            int remainingLength = code.Length - i;                   // Count of remaining characters
            if (remainingLength < 1)
                throw new InvalidProgramException("Code128: bad content length, the rest of text for processing is empty.");

            // 1. Fixed Set:
            if (codeSet == Code128Set.SetA || codeSet == Code128Set.SetB || codeSet == Code128Set.SetC)
            {
                if (codeSet == Code128Set.SetC && remainingLength < 2)
                    throw new InvalidProgramException("Code128C: bad content length, the rest of text for processing is smaller than 2 chars.");
                
                if (this.TryGetEncodeInfoForSet(code, i, codeSet, out result))
                    return result;

                throw new InvalidProgramException("Code128 (" + codeSet.ToString() + "): invalid content, text \"" + code.Substring(i) + "\" can not be processed.");
            }

            // 2. It is advisable to go to the SetC (Numeric encoding) from any other set?  
            //    Yes, if remaining of text is length of 4 characters and this 4 chars contains only digits:
            //     Then we will encoding: 1 pattern for change encoding, 2 patterns for 2 pair of digits, and at worst 1 pattern for change encoding to A or B for subsequent characters:
            if (currSet != Code128Set.SetC && remainingLength >= 4 && ContainOnlyDigit(code.Substring(i, 4)))
            {
                if (this.TryGetEncodeInfoForSet(code, i, Code128Set.SetC, out result))
                    return result;
                throw new InvalidProgramException("Code128 (SetC): invalid program, numbers \"" + code.Substring(i,2) + "\" can not be processed.");
            }

            // 3. It is possible to continue in the same CodeSet?
            if (currSet == Code128Set.SetA || currSet == Code128Set.SetB || currSet == Code128Set.SetC)
            {
                if (this.TryGetEncodeInfoForSet(code, i, currSet, out result))
                    return result;
            }

            // 4. Current CodeSet is not usable (we must detect new codeSet), find another CodeSet for current code:
            if (remainingLength >= 2 && ContainOnlyDigit(code.Substring(i, 2)))
            {   // If subsequent chars are 2 digit, then switch to SetC is appropriate:
                if (this.TryGetEncodeInfoForSet(code, i, Code128Set.SetC, out result))
                    return result;
            }
            // Now we will search (test for every encoding set) for longest code, which can this set encode:
            int countA = this.GetEncodedCountForSet(code, i, Code128Set.SetA);
            int countB = this.GetEncodedCountForSet(code, i, Code128Set.SetB);
            int countC = this.GetEncodedCountForSet(code, i, Code128Set.SetC);
            if ((countC >= countA && countC >= countA) && (this.TryGetEncodeInfoForSet(code, i, Code128Set.SetC, out result))) return result;    // SetC produce 2x smaller code than SetA and SetB.
            if ((countA >= countB) && (this.TryGetEncodeInfoForSet(code, i, Code128Set.SetA, out result))) return result;                        // SetA is preferred to SetB
            if ((countB > countA) && (this.TryGetEncodeInfoForSet(code, i, Code128Set.SetB, out result))) return result;
            throw new InvalidProgramException("Code128: invalid content, character \"" + code.Substring(i, 1) + "\" can not be processed.");
        }
        /// <summary>
        /// Determine count of character in subsequent code, which can be encoded by specified CodeSet.
        /// </summary>
        /// <param name="code"></param>
        /// <param name="i"></param>
        /// <param name="testSet"></param>
        /// <returns></returns>
        protected int GetEncodedCountForSet(string code, int i, Code128Set testSet)
        {
            int result = 0;
            int step = ((testSet == Code128Set.SetA || testSet == Code128Set.SetB) ? 1 : (testSet == Code128Set.SetC ? 2 : 0));
            if (step > 0)
            {
                int length = code.Length;
                int p = i;
                while ((p + step) <= length)
                {
                    string codeKey = code.Substring(p, step);
                    if (!this.ContainsKey(testSet, codeKey))
                        break;
                    result += step;
                    p += step;
                }
            }
            return result;
        }
        /// <summary>
        /// Try get CodeInfo for code, and CodeSet.
        /// </summary>
        /// <param name="code"></param>
        /// <param name="i"></param>
        /// <param name="nextSet"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        protected bool TryGetEncodeInfoForSet(string code, int i, Code128Set nextSet, out KeyValuePair<Code128Key, Code128Item> result)
        {
            int remainingLength = code.Length - i;
            if (nextSet == Code128Set.SetC && remainingLength < 2)
            {
                result = new KeyValuePair<Code128Key, Code128Item>();
                return false;
            }
            string codeKey = code.Substring(i, (nextSet == Code128Set.SetC ? 2 : 1));          // SetA and SetB = 1 character, SetC = 2 characters.
            return this.TryGetInfo(nextSet, codeKey, out result);
        }
        /// <summary>
        /// Stored into the buffer change of the active codeSet.
        /// </summary>
        /// <param name="codeSet">Required CodeSet (Encoding CodeType A/B/C)</param>
        /// <param name="currSet">Currently active set</param>
        /// <param name="nextSet">Future set</param>
        /// <param name="checkSum">CheckSum for CheckDigit</param>
        /// <returns></returns>
        protected Code128Set StoreCodeSetToBuffer(Code128Set codeSet, Code128Set currSet, Code128Set nextSet, int position, Code128CheckSum checkSum)
        {
            Code128Key key = this.CreateKeyForCode(currSet, nextSet);

            KeyValuePair<Code128Key, Code128Item> value;
            if (!this.TryGetInfo(key, out value))
                throw new InvalidProgramException("Code128: can not found CodePattern for Key=" + key.ToString() + ".");

            this.StoreItemToBuffer(value.Key, value.Value, checkSum);
            return nextSet;
        }
        /// <summary>
        /// Add pattern for CheckDigit
        /// </summary>
        /// <param name="checkSum"></param>
        protected void AddItemForCheckDigit(Code128CheckSum checkSum)
        {
            int checkNumber = checkSum.CheckNumber;
            Code128Item item;
            if (!this.Code128Items.TryGetValue(checkNumber, out item))
                throw new InvalidProgramException("Code128: can not found CodePattern for CheckDigit=" + checkNumber.ToString() + ".");
            this.StoreItemToBuffer(null, item, null);
        }
        /// <summary>
        /// Store into the buffer specified data.
        /// Add value of pattern into CheckSum.
        /// </summary>
        /// <param name="item"></param>
        /// <param name="checkSum"></param>
        protected void StoreItemToBuffer(Code128Set codeSet, string codeKey, Code128CheckSum checkSum)
        {
            this.StoreItemToBuffer(new Code128Key(codeSet, codeKey), checkSum);
        }
        /// <summary>
        /// Store into the buffer specified data.
        /// Add value of pattern into CheckSum.
        /// </summary>
        /// <param name="item"></param>
        /// <param name="checkSum"></param>
        protected void StoreItemToBuffer(Code128Key key, Code128CheckSum checkSum)
        {
            KeyValuePair<Code128Key, Code128Item> value;
            if (!this.TryGetInfo(key, out value))
                throw new InvalidProgramException("Code128: can not found CodePattern for Key=" + key.ToString() + ".");

            this.StoreItemToBuffer(value.Key, value.Value, checkSum);
        }
        /// <summary>
        /// Store into the buffer specified data.
        /// Add value of pattern into CheckSum.
        /// </summary>
        /// <param name="item"></param>
        /// <param name="checkSum"></param>
        protected void StoreItemToBuffer(Code128Key key, Code128Item item, Code128CheckSum checkSum)
        {
            this.Keys.Add(key);
            this.AddTextToBuffer(item.Pattern);
            if (checkSum != null)
                checkSum.Add(item.Value);
        }
        /// <summary>
        /// Create key (Code128Key) for switch from current set (currSet) to next set (nextSet).
        /// When current set is None, return a key type "START_x", in other cases return key type "CODE_x".
        /// </summary>
        /// <param name="currSet"></param>
        /// <param name="nextSet"></param>
        /// <returns></returns>
        protected Code128Key CreateKeyForCode(Code128Set currSet, Code128Set nextSet)
        {
            bool isStart = (currSet == Code128Set.None);             // true: START_x (first word of code); false: CODE_x (change of codeset)
            switch (nextSet)
            {
                case Code128Set.SetA:
                    return (isStart ? 
                        new Code128Key(Code128Set.SetA, START_A) : 
                        new Code128Key(currSet, CODE_A));
                case Code128Set.SetB:
                    return (isStart ? 
                        new Code128Key(Code128Set.SetB, START_B) : 
                        new Code128Key(currSet, CODE_B));
                case Code128Set.SetC:
                    return (isStart ? 
                        new Code128Key(Code128Set.SetC, START_C) : 
                        new Code128Key(currSet, CODE_C));
            }
            throw new InvalidProgramException("Code128: can not create Key for NextSet=" + nextSet.ToString() + ".");
        }
        /// <summary>
        /// Debug info: sequence of keys (Set + Code)
        /// </summary>
        protected List<Code128Key> Keys;
        #endregion
    }
	#endregion
}
